package com.zxt.web.database.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author zxt
 */
public class TwitterSnowFlake {
    private static final Logger log = LoggerFactory.getLogger(TwitterSnowFlake.class);
    private long timestamp;
    private long worker;
    private long sequence;

    public TwitterSnowFlake() {
        this(System.currentTimeMillis() & 1023L);
    }

    public TwitterSnowFlake(long worker) {
        if (worker >= 0L && 1023L >= worker) {
            log.info("TwitterSnowFlake's worker is {}.", worker);
            this.timestamp = System.currentTimeMillis();
            this.worker = worker;
            this.sequence = 0L;
        } else {
            throw new IllegalArgumentException("worker Id can't be less than 0 or greater than 1023");
        }
    }

    public synchronized long next() {
        long now = System.currentTimeMillis();
        if (now > this.timestamp) {
            this.timestamp = now;
            this.sequence = 0L;
        } else if (now == this.timestamp) {
            ++this.sequence;
            if (this.sequence > 4095L) {
                this.timestamp = this.tilNextMillis(this.timestamp);
                this.sequence = 0L;
            }
        } else if (now < this.timestamp) {
            log.error("Clock is moving backwards. Rejecting requests until {}.", this.timestamp);
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to create for %d milliseconds", this.timestamp - now));
        }

        return this.create(this.timestamp, this.sequence);
    }

    private long create(long timestamp, long sequence) {
        return timestamp - 1451577600000L << 22 | this.worker << 12 | sequence;
    }

    private long tilNextMillis(long timestamp) {
        long now;
        for (now = System.currentTimeMillis(); now <= timestamp; now = System.currentTimeMillis()) {
        }

        return now;
    }
}
